/**
 * @Author: Kritsu
 * @Date:   2021/11/12 16:46:12
 * @Last Modified by:   Kritsu
 * @Last Modified time: 2021/11/17 18:37:01
 */
import { ref, inject, App, Ref, computed } from "vue"

interface Language {
    locale: string
    dictionaries: Record<string, string>
}

interface LanguageOptions {
    locale: string
    dictionaries: any
}

const PARAMS_REGEX = /\[\w+(=\w+)?(,\w+(=\w+)?)*\]/g
const KEY_REGEX = /{\$?\w+(\.\w+)*(\[\w+(=\w+)?(,\w+(=\w+)?)*\])?}/g

class Translator {
    locale: Ref<string>
    languages: Ref<Language[]>
    current_dictionary: Ref<Record<string, string>>

    constructor({ locale, data }: TranslatorOptions) {
        this.locale = ref<string>(locale ?? "zh-cn")
        this.languages = ref<Language[]>([])
        this.current_dictionary = computed(() => this.getDictionary(this.locale.value))
        data.forEach(info => this.pushData(info))
    }

    async pushData({ locale, dictionaries }: LanguageOptions) {
        dictionaries = flat(dictionaries) as Record<string, string>
        this.languages.value.push({ locale, dictionaries })
    }

    getDictionary(locale: string) {
        let lan: Language | undefined = this.languages.value.find(e => e.locale == locale)
        if (!lan) {
            lan = { locale, dictionaries: {} }
            this.languages.value.push(lan)
        }
        return lan.dictionaries
    }

    setLocale(locale_value: string) {
        this.locale.value = locale_value
    }

    getKey(key: string): string | undefined {
        const lan = this.current_dictionary.value
        if (!lan || !!lan[key]) {
            return key
        }
        const keys = Object.keys(lan)
        for (let e of keys) {
            if (!/{[\w]+}/g.test(e)) {
                continue
            }
            const reg = new RegExp(`^${e.replace(/({\w+})/g, "([a-zA-Z0-9-\\s_]+)")}$`)
            if (reg.test(key)) {
                const names = e.match(/{(\w+)}/g) ?? []
                const result = key.match(reg) ?? []
                let args: Record<string, string> = {}

                let prefix = ""
                const last = key.lastIndexOf(".")
                if (last > 0) {
                    prefix = key.slice(0, last)
                }
                for (let i = 0; i < names.length; i++) {
                    let name = names[i].slice(1, -1)
                    args[name] = this.getTrans(prefix.concat(".").concat(result[i + 1]))
                }
                let value = lan[e]

                value = format_string(value, args)
                lan[key] = value
                return key
            }
        }
    }

    getTrans(key?: string): string {
        if (!key) {
            return ""
        }
        let lan = this.current_dictionary.value

        if (!!lan) {
            if (KEY_REGEX.test(key)) {
                key = key.replace(KEY_REGEX, match => {
                    match = match.slice(1, -1)

                    const params: Record<string, any> = {}
                    const args: string[] = []

                    let paramsKey = match.match(PARAMS_REGEX)
                    let str = match

                    if (paramsKey?.length) {
                        //处理参数
                        str = str.replace(PARAMS_REGEX, () => "")
                        for (let p of paramsKey) {
                            if (!p) {
                                continue
                            }
                            let s = p.slice(1, -1)

                            const arr = s.split(",")
                            for (let i of arr) {
                                if (i.includes("=")) {
                                    const [k, v] = i.split("=")
                                    params[k] = v
                                } else {
                                    args.push(i)
                                }
                            }
                        }
                    }
                    return format_string(this.getTrans(str), params, ...args)
                })
            } else {
                //处理作用域
                const nodes = key.split(".")

                if (nodes.length > 1) {
                    let i = nodes.length - 1
                    const last = nodes[i]
                    while (i > -1) {
                        let node_key = nodes.slice(0, i).concat(last).join(".")
                        let get_key = this.getKey(node_key)
                        if (get_key) {
                            key = get_key
                            break
                        }
                        i--
                    }
                }
                key = lan[key] ?? key
            }
        }
        return key
    }

    compile(content: string, ...prefix: (string | undefined)[]) {
        let key = compileKey(content, ...prefix)
        content = this.getTrans(key) ?? key
        return content
    }

    install(app: App) {
        app.provide(TranslatorSymbol, this)
        app.directive("trans", (el, { value, arg }) => {
            let key = value ?? el.textContent
            el.textContent = this.compile(key, arg)
        })
    }
}

interface TranslatorOptions {
    locale?: string
    data: LanguageOptions[]
}

const TranslatorSymbol = Symbol("[Translator]instance")

function flat(obj: any, prefix: string = ""): any {
    const rs: any = {}
    for (let key in obj) {
        let value = obj[key]
        const curKey = !!prefix ? prefix.concat(".").concat(key) : key
        if (typeof value == "object") {
            Object.assign(rs, flat(value, curKey))
        } else {
            rs[curKey] = value
        }
    }
    return rs
}

export function defineTranslator(options: TranslatorOptions) {
    return new Translator(options)
}

function compileKey(key?: string, ...prefix: (string | undefined)[]) {
    prefix = prefix.filter(e => !!e)
    if (!!key && KEY_REGEX.test(key)) {
        key = key.replace(KEY_REGEX, match => {
            let str = match.slice(1, -1)
            str = compileKey(str, ...prefix) ?? ""
            return `{${str}}`
        })
    } else if (!key?.startsWith("$")) {
        key = [...prefix, key].join(".")
    } else {
        key = key.slice(1)
    }
    return key
}

export function useTrans(...prefix: string[]) {
    const state = inject<Translator>(TranslatorSymbol)
    return function (key?: string): string {
        return key ? state?.compile(key, ...prefix) ?? key : ""
    }
}

export function useTranslator() {
    return inject(TranslatorSymbol) as Translator
}

export function format_string(str: string, params: any = {}, ...args: any[]) {
    let num = 0
    str = str.replace(/{}/g, match => {
        return args[num++] ?? match
    })
    str = str.replace(/{\d+}/g, match => {
        const i = parseInt(match.slice(1, -1))
        return args[i] ?? match
    })
    str = str.replace(/{\w+}/g, match => {
        const name = match.slice(1, -1)
        return params[name] ?? match
    })
    return str
}
